/* 						-*- coding: utf-8 -*-
 * Une version de toplevel
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include "toplevel.h"
#include "editline.h"
#include "tokenize.h"
#include "intern.h"
#include "environ.h"
#include "history.h"

#define MAX_BUFFER 256
#define MAX_TOKENS 50

#define NO_REG -1
#define NO_IXUSR -2
#define NO_IXGRP -3
#define NO_IXOTH -4

struct token* tokens[MAX_TOKENS];
int tokens_size;
int tokens_index;

struct command {
	int argc;
	char** argv;
	char *file_in;
	char *file_out;
	int fd_in;
	int fd_out;
	int append;
	int background;
};

void init_command (struct command* cmd) {
	cmd->argc = 0;
	cmd->argv = (char **)calloc(tokens_size-tokens_index+2, sizeof(char*));
	cmd->file_in = NULL;
	cmd->file_out = NULL;
	cmd->fd_in = STDIN_FILENO;
	cmd->fd_out = STDOUT_FILENO;
	cmd->append = 0;
	cmd->background = 0;
	
	print_command(cmd, "init_command");
}

void print_command (struct command* cmd, const char* name) {
    return;
    printf("\n####### PRINT COMMAND \"%s\" ##########\n", name);
	printf("# cmd->argc=%d\n", cmd->argc);
	int i;
    for(i = 0; i < cmd->argc; i++)
        printf("# cmd->argv[%d]=%s\n", i, cmd->argv[i]);	
	printf("# cmd->file_in=%s\n", cmd->file_in);
    printf("# cmd->file_out=%s\n", cmd->file_out);    
	printf("# cmd->fd_in=%d   STDIN_FILENO=%d\n", cmd->fd_in, STDIN_FILENO);
    printf("# cmd->fd_out=%d  STDOUT_FILENO=%d\n", cmd->fd_out, STDOUT_FILENO);
    printf("###################################\n\n");
}


int is_executable(const struct stat b_stat) {
    if (!S_ISREG(b_stat.st_mode)) return NO_REG;
    if (b_stat.st_uid == getuid() && !(b_stat.st_mode & S_IXUSR)) return NO_IXUSR;
    if (b_stat.st_gid == getgid() && !(b_stat.st_mode & S_IXGRP)) return NO_IXGRP;
    if (!(b_stat.st_mode & S_IXOTH)) return NO_IXOTH;
    return 0;
}

const char* get_path_exe(const char* command, const char* env_path) {
	char* path = (char*) malloc(MAX_BUFFER * sizeof(char));
	char* token;
	char path_separator = ':';
	struct stat b_stat;

	if (stat(command, &b_stat) == 0 && is_executable(b_stat) == 0)
		return command; /* Si c'est un fichier exécutable : gagné ! */

	for (token = strtok(strdup(env_path), &path_separator); token != NULL; token = strtok(NULL, &path_separator)) {
		strcpy(path, "\0");
		strncat(path, token, MAX_BUFFER);
		strncat(path, "/", MAX_BUFFER);
		strncat(path, command, MAX_BUFFER);
		if (stat(path, &b_stat) == 0 && is_executable(b_stat) == 0)
			return path;
	}
	free(path);
	return NULL; /* On n'a pas trouvé d'exécutable */
}



void execute (struct command* cmd) {
    if(!cmd->argc){
	    printf("Error no command\n");
	    return;
    }
    
    /* Gestion des redirections */
    int fd_in = STDIN_FILENO;
    int fd_out = STDOUT_FILENO;
    if (cmd->file_in){ 
        if(-1 == (cmd->fd_in = open(cmd->file_in, O_RDONLY)))
            printf("error open read file : %s\n", cmd->file_in);
        else {
            printf("open in\n"); 
            fd_in = dup(STDIN_FILENO);
            dup2(cmd->fd_in, STDIN_FILENO);
        }
    }
        
    if (cmd->file_out){   
        if(-1 == (cmd->fd_out = open(cmd->file_out, 
                                O_WRONLY | O_CREAT | (cmd->append ? O_APPEND : O_TRUNC), 
                                S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)))
            printf("error open write file : %s\n", cmd->file_out);
        else {
            printf("open out\n"); 
            fd_out = dup(STDOUT_FILENO);
            dup2(cmd->fd_out, STDOUT_FILENO);   
        }
    }

    /* Execution de la commande */
    internal_cmd internal = find_internal(cmd->argv[0]);
    if(internal) {
   	    internal(cmd->argc, cmd->argv);
    }
    else {
	    pid_t pid;
	    if(!(pid = fork())) {    
		    signal(SIGINT, SIG_DFL);  		    
		    char* path = (char*)get_path_exe(cmd->argv[0], get_environ("PATH"));
		    execve(path, cmd->argv, Environ);
	    } else {
            int status;
            waitpid(pid, &status, 0);
	    }
    }
    
    if (STDIN_FILENO != cmd->fd_in) {
        close(cmd->fd_in);
        dup2(fd_in, STDIN_FILENO);
    }
    if (STDOUT_FILENO != cmd->fd_out) {
        close(cmd->fd_out);
        dup2(fd_out, STDOUT_FILENO);
    }
    
    init_command(cmd);
}



void toplevel(FILE *f) {
	char line[MAX_BUFFER];
	signal(SIGINT, SIG_IGN);
	
	while(1) {
		tokens_size = 0;
		tokens_index = 0;
	  
	  
		if (f == stdin) {
		    editline(line, MAX_BUFFER, get_environ("PROMPT"));
		} else {
		    char* retour = fgets(line, MAX_BUFFER, f);
		    if (retour == NULL) {
			break;
		    }
		}

		if (line == NULL) break;
		if (strlen(line) == 0 || get_token(line)->type == eol) continue;
		add_history(line);
		
        struct token *tk;	
		for(tk = get_token(line); eol != tk->type; tk = get_token(NULL), tokens_size++) {
		    tokens[tokens_size] = (struct token*)malloc(sizeof(struct token*));
		    tokens[tokens_size] = tk;
		}
		tokens[tokens_size] = (struct token*)malloc(sizeof(struct token*));
		tokens[tokens_size]->type = eol;
		tokens[tokens_size++]->content = NULL;
		
		
        struct command* cmd = (struct command*)malloc(sizeof(struct command));
        init_command(cmd);
        
		for(tokens_index = 0; tokens_index < tokens_size; tokens_index++) {
		    /*
			printf("tokens--[%d]=%s tokens_size=%d   type=", tokens_index, tokens[tokens_index]->content, tokens_size); 
			*/
			switch(tokens[tokens_index]->type) {
				case item:			 
					cmd->argv[cmd->argc++] = strdup (tokens[tokens_index]->content);					
					break;
				case less:
					tokens_index++;
				  	if (tokens_index < tokens_size && tokens[tokens_index]->type == item) { 	
					      	cmd->file_in = strdup (tokens[tokens_index]->content);
					      	
					} else {
					  /* strcpy(error, "less error"); */
					  printf("less error\n");
					}
					break;
				case greater:
					tokens_index++;
				  	if (tokens_index < tokens_size && tokens[tokens_index]->type == item) {
				  	        cmd->file_out = strdup (tokens[tokens_index]->content);
					} else {
					  /* strcpy(error, "less error"); */
					  printf("greater error\n");
					}
					break;
				case append:
					tokens_index++;
				  	if (tokens_index < tokens_size && tokens[tokens_index]->type == item) {
				  	        cmd->append = 1;
				  	        cmd->file_out = strdup (tokens[tokens_index]->content);
					} else {
					  /* strcpy(error, "less error"); */
					  printf("append error\n");
					}
					break;
				case amper:
				    tokens_index++;
				  	if (tokens_index < tokens_size && tokens[tokens_index]->type == item) {
					    cmd->background = 1;
						execute(cmd);
					} else {
					  /* strcpy(error, "less error"); */
					  printf("amper error\n");
					}
					break;
				case semicolon:
				case eol:
					execute(cmd);
					break;	
			}
		}		
	}
	
	for(tokens_index = 0; tokens_index < tokens_size; tokens_index++)
	  free(tokens[tokens_index]);

	printf("Bye\n");
	exit(0);

}
